open Cast

let string_of_cop o = match o with
	  CAdd -> "+"
	| CSub -> "-"
	| CMult -> "*"
	| CDiv -> "/"
	| CEqual -> "=="
	| CNeq -> "!="
	| CLess -> "<"
	| CLeq -> "<="
	| CGreater -> ">"
	| CGeq -> ">="
	| CAnd -> "&&"
	| COr -> "||"
	| CMod -> "%"

let rec string_of_ctype t = match t with
	  CVoid -> "void"
	| CInt -> "int"
	| CFloat -> "double"
	| CString -> "string"
	| CDiGrNode -> "DiGrNode"
	| CDiGrEdge -> "DiGrEdge"
	| CTypePointer p -> string_of_ctype p ^ "*"
	| CVector v -> "deque<" ^ string_of_ctype v ^ ">"


let string_type_of_formal (s : csigvar) = match s with
	  CSigVar (t, n) -> (string_of_ctype t)
	|  CSigVect (t, n) -> "vector<" ^ (string_of_ctype t) ^ ">"
	|  CSigPtr (t, n) -> (string_of_ctype t) ^ "*"
	|  CSigRef (t, n) -> "&" ^ (string_of_ctype t)
	|  CSigArr (t, n) -> string_of_ctype t
	| CFuncFormal (t, n, a) -> string_of_ctype t

let rec string_of_cvar v = match v with
	  Cvar s-> s 
	| CArrayStat (n, i) -> " " ^ n ^ "[" ^ string_of_int i ^ "]" 
	| CArrayDyn (n, i) -> " " ^ n ^ "[" ^ (string_of_cvar i) ^ "]"
	| CPointer s -> " *" ^ s 



let rec string_of_csigvar ( s : csigvar) = match s with
	  CSigVar (t, s) -> (string_of_ctype t) ^ " " ^ s
	| CSigPtr (t ,s) -> (string_of_ctype t) ^ " *" ^ s
	| CSigArr (t, a) -> (string_of_ctype t) ^ " " ^ 
	(
		match a with
		Cvar s -> s
		| CArrayStat (s, i) -> s ^ "[" ^ string_of_int i ^ "]"
		| CArrayDyn (s, c) -> s ^ "[" ^ string_of_cvar c ^ "]"
		| CPointer (n) -> (print_endline ("ERROR: tried to make pointer to indexed array" ^ n); "BAD")
	)
	| CSigRef (t, s) -> (string_of_ctype t) ^ " &" ^ s
	| CSigVect (t, s) -> "vector<" ^ (string_of_ctype t) ^ "> " ^ s
	| CFuncFormal (t, s, a) -> (string_of_ctype t) ^ " (*" ^ s ^")(" ^
		(
		if ((List.length a) > 0) then
		    (List.fold_left (fun b c -> b ^ ", " ^ (string_of_ctype c)) (string_of_ctype (List.hd a)) (List.tl a) )
		else ""
		) ^

		")"

let array_name v = match v with
	  CArrayStat (n, i) -> n
	| CArrayDyn (n, i) -> n
	| Cvar (n) -> ( print_endline ("ERROR! " ^ n ^ " is a variable and not an array!"); "BAD")
	| CPointer (n) -> ( print_endline ("ERROR! " ^ n ^ " is a pointer and not an array!"); "BAD")

let array_size v = match v with
	  CArrayStat (n, i) -> i
	| CArrayDyn (n, i) -> int_of_string (string_of_cvar i)
	| Cvar (n) -> ( print_endline ("ERROR! " ^ n ^ " is a variable and not an array!"); -1)
	| CPointer (n) -> ( print_endline ("ERROR! " ^ n ^ " is a pointer and not an array!"); -1)

let signature_of_fdecl f =
	(string_of_ctype f.creturntype) ^ " " ^ f.cfname ^ "(" ^
		(
		if ((List.length f.cformals) > 0) 
		then
		    (List.fold_right 	(fun a b -> b ^ ", " ^ (string_type_of_formal a)) 
					(List.tl f.cformals) 
					(string_type_of_formal (List.hd f.cformals))
			)
		else ""
		) 
		^");\n\n"

let rec string_of_cexpr  e = match e with 
	  CLiteral_Int i -> " "^string_of_int i^" "
	| CLiteral_Float f-> " "^string_of_float f^ " " 
	| CLiteral_String s -> s
	| CActuals a -> 
		"{" ^ (
			List.fold_left 	(fun b a -> (string_of_cexpr a) ^ ", " ^ b) 
					(string_of_cexpr (List.hd a)) 
					(List.tl a)
		) ^ "}"
	| CId s -> (string_of_cvar s)
	| CBinop (e1, o, e2) -> (string_of_cexpr e1) ^ " " ^ (string_of_cop o) ^ " " ^ (string_of_cexpr e2)
	| CCall (s, l) -> 
		s ^ "(" ^
		(
		if ((List.length l) > 0) 
		then
			(List.fold_left (fun b a -> b ^ ", " ^ (string_of_cexpr a)) 
					(string_of_cexpr (List.hd l)) 
					(List.tl l))
		else ""
		) ^ ")"
	| CCallNew (s, l) -> "new " ^ string_of_cexpr (CCall (s,l))
	| CObjCall (os, s, l) -> (string_of_cvar os) ^ "->" ^ string_of_cexpr (CCall(s,l))
	| CNoexpr -> "/* caught a NOEXPR! */"
	| CIdAddr s -> " &" ^ s ^ " "

let rec init_nodes name size =
	if size == 0 then name ^ "[" ^ (string_of_int size) ^ "] = new DiGrNode();\n" 
	else name ^ "[" ^ (string_of_int size) ^ "] = new DiGrNode();\n" ^ (init_nodes name (size - 1))

let rec string_of_cstmt s = match s with 
	CTryCatchBlock stmtlist -> "try{\n" ^
	(
		if List.length stmtlist > 0 then
		   (List.fold_left 	(fun b a -> b ^ (string_of_cstmt a)) 
					(string_of_cstmt (List.hd stmtlist)) 
					(List.tl stmtlist) )
		else ""
	) ^ "}\ncatch(const char *e) {\nstd::cout << e << std::endl;\n}\n"

	| CPrint l -> "std::cout << " ^ (List.fold_right (fun a b -> (string_of_cexpr a) ^ " << " ^ b ) l "std::endl;\n")
	| CExpr e -> (string_of_cexpr e) ^ ";\n"
	| CDeclare s -> (string_of_csigvar s) ^ ";\n"
	| CAssignRule (s, e) -> s ^ " = " ^ (string_of_cexpr e) ^ ";\n"
	| CAssign (s, e) -> (string_of_cvar s) ^ "=" ^ (string_of_cexpr e) ^ ";\n"
	| CWhile (e, s) -> 
		"while(" ^ (string_of_cexpr e) ^ ")\n{" ^ 
		(
		if List.length s > 0 
		then
		   (List.fold_left (fun b a -> b ^ (string_of_cstmt a)) 
					(string_of_cstmt (List.hd s)) 
					(List.tl s) )
		else ""
		) ^ "}" 
		| CIf (e,s1,s2 ) -> "if("  ^ (string_of_cexpr e) ^ ")\n{" ^ 
		(
			if List.length s1 > 0
			then
			   (List.fold_left 	(fun b a -> b ^ (string_of_cstmt a)) 
						(string_of_cstmt (List.hd s1)) 
						(List.tl s1) )
			else ""
		)
		 ^ "}\n" 
			^ "else{" ^
		(
		if List.length s2 > 0 
		then
			(List.fold_right 	(fun a b -> (string_of_cstmt a) ^ b) 
						(List.tl s2) 
						(string_of_cstmt (List.hd s2)) )
		else ""
		)
		 ^"}\n" 
		| CBlock s -> 
		    ( if List.length s > 0 then
		      (List.fold_left (fun b a -> b ^ (string_of_cstmt a)) (string_of_cstmt (List.hd s)) (List.tl s) )
		else ""
		   )
	| CReturn e -> "return " ^ (string_of_cexpr e) ^";\n"
	| CDeclareAssign (s,e) -> (string_of_csigvar s) ^ " = " ^ (string_of_cexpr e) ^ ";\n"
	| CDoWhile (e, s) -> "do {\n" ^
		(
		if List.length s > 0 
		then
		      (List.fold_left 	(fun b a -> b ^ (string_of_cstmt a)) 
					(string_of_cstmt (List.hd s)) 
					(List.tl s))
		else ""
		) ^ "} while (" ^ (string_of_cexpr e) ^ ");\n"
		


let string_of_c_fdecl cf = 
	(string_of_ctype cf.creturntype) ^ " " ^ cf.cfname ^ "(" ^
	(
		if ((List.length cf.cformals) > 0) 
		then
		    (List.fold_left 	(fun b a -> b ^ ", " ^ (string_of_csigvar a))
					(string_of_csigvar (List.hd cf.cformals)) 
					(List.tl cf.cformals) )
		else ""
	)
	^") {\n" ^
	   (List.fold_right 	(fun a b -> (string_of_cstmt a) ^ b) 
				cf.cbody 
				"" )   
	^ "\n}\n\n"

let string_c_includes = "#include \"digr.h\"\n#include <iostream>\n"


let string_of_c_program ( p : cprogram) = 
	match p with
	(cfdecllist) ->
		"/* actual definition of C++ functions */\n" ^
		(List.fold_right (fun a b -> (string_of_c_fdecl a) ^ b) cfdecllist "" )

let _ =
	print_endline "/*begin formal AST verification";
	let lexbuf = Lexing.from_channel stdin in
	let program = Parser.program Scanner.token lexbuf in
	let error = Interpret.check_ast program
	in
	let c_program =
	(

		print_endline "begin translation to CAST";
		Translate.cast_from_ast program
	)
	in 
	if error then
	(
		print_endline "======================";
		print_endline "FAILED STATIC SEMANTIC CHECK";
		print_endline "NO TARGET LANGUAGE OUTPUT */"

	)
		else (
		print_endline "passed static semantic checking, begin code generation";
		print_endline "====================== */";
		print_endline (string_c_includes ^ (string_of_c_program c_program))
	)
